﻿using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.OpenApi.Models;
using YDT_ABPvNext_Projects.Data;
using YDT_ABPvNext_Projects.Localization;
using Volo.Abp;
using Volo.Abp.Uow;
using Volo.Abp.Account;
using Volo.Abp.Account.Web;
using Volo.Abp.AspNetCore.MultiTenancy;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared;
using Volo.Abp.AspNetCore.Serilog;
using Volo.Abp.AuditLogging.EntityFrameworkCore;
using Volo.Abp.Autofac;
using Volo.Abp.AutoMapper;
using Volo.Abp.Emailing;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.MySQL;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Identity;
using Volo.Abp.Identity.EntityFrameworkCore;
using Volo.Abp.Localization;
using Volo.Abp.Localization.ExceptionHandling;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.PermissionManagement;
using Volo.Abp.PermissionManagement.EntityFrameworkCore;
using Volo.Abp.PermissionManagement.HttpApi;
using Volo.Abp.PermissionManagement.Identity;
using Volo.Abp.PermissionManagement.OpenIddict;
using Volo.Abp.Swashbuckle;
using Volo.Abp.TenantManagement;
using Volo.Abp.TenantManagement.EntityFrameworkCore;
using Volo.Abp.OpenIddict;
using Volo.Abp.Security.Claims;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.Validation.Localization;
using Volo.Abp.VirtualFileSystem;
using AbpVnext_Module.DBContext;
using AbpVnext_Module.ExceptionFillter;
using AbpVnext_Module.Permission;
using AbpVnext_Module;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using System.Net;
using System.Text;
using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.Authorization.Permissions;
using AbpVnext_Module.Controllers;
using System.Reflection.Emit;
using Volo.Abp.AuditLogging;
using Polly;
using Microsoft.AspNetCore.Mvc;

namespace YDT_ABPvNext_Projects;

[DependsOn(
    // ABP Framework packages
    typeof(AbpAspNetCoreMvcModule),
    typeof(AbpAspNetCoreMultiTenancyModule),
    typeof(AbpAutofacModule),
    typeof(AbpAutoMapperModule),
    typeof(AbpEntityFrameworkCoreMySQLModule),
    typeof(AbpAspNetCoreMvcUiLeptonXLiteThemeModule),
    typeof(AbpSwashbuckleModule),
    typeof(AbpAspNetCoreSerilogModule),

    // Account module packages
    typeof(AbpAccountApplicationModule),
    typeof(AbpAccountHttpApiModule),
    typeof(AbpAccountWebModule),

    // Identity module packages
    typeof(AbpPermissionManagementDomainIdentityModule),
    typeof(AbpPermissionManagementDomainOpenIddictModule),
    typeof(AbpIdentityApplicationModule),
    typeof(AbpIdentityHttpApiModule),
    typeof(AbpIdentityEntityFrameworkCoreModule),

    // Audit logging module packages
    typeof(AbpAuditLoggingEntityFrameworkCoreModule),

    // Permission Management module packages
    typeof(AbpPermissionManagementApplicationModule),
    typeof(AbpPermissionManagementHttpApiModule),
    typeof(AbpPermissionManagementEntityFrameworkCoreModule),

    // Tenant Management module packages
    typeof(AbpTenantManagementApplicationModule),
    typeof(AbpTenantManagementHttpApiModule),
    typeof(AbpTenantManagementEntityFrameworkCoreModule)

)]
public class YDT_ABPvNext_ProjectsModule : AbpModule
{
    /* Single point to enable/disable multi-tenancy */
    private const bool IsMultiTenant = true;

    public override void PreConfigureServices(ServiceConfigurationContext context)
    {
        var hostingEnvironment = context.Services.GetHostingEnvironment();
        var configuration = context.Services.GetConfiguration();

        context.Services.PreConfigure<AbpMvcDataAnnotationsLocalizationOptions>(options =>
        {
            options.AddAssemblyResource(
                typeof(YDT_ABPvNext_ProjectsResource)
            );
        });

        PreConfigure<OpenIddictBuilder>(builder =>
        {
            //builder.AddValidation(options =>
            //{
            //    options.AddAudiences("YDT_ABPvNext_Projects");
            //    options.UseLocalServer();
            //    options.UseAspNetCore();
            //});
        });

        if (!hostingEnvironment.IsDevelopment())
        {
            //PreConfigure<AbpOpenIddictAspNetCoreOptions>(options =>
            //{
            //    options.AddDevelopmentEncryptionAndSigningCertificate = false;
            //});

            //PreConfigure<OpenIddictServerBuilder>(serverBuilder =>
            //{
            //    serverBuilder.AddProductionEncryptionAndSigningCertificate("openiddict.pfx", "fdc17aff-940a-4e6b-aa09-334b95127331");
            //});
        }

        YDT_ABPvNext_ProjectsGlobalFeatureConfigurator.Configure();
        YDT_ABPvNext_ProjectsModuleExtensionConfigurator.Configure();
        YDT_ABPvNext_ProjectsEfCoreEntityExtensionMappings.Configure();
    }

    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        var hostingEnvironment = context.Services.GetHostingEnvironment();
        var configuration = context.Services.GetConfiguration();

        if (hostingEnvironment.IsDevelopment())
        {
            context.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>());
        }
        //配置授权
        ConfigureAuthentication(context);
        //怕配置全局css文件
        //ConfigureBundles();
        //配置多租户
        ConfigureMultiTenancy();
        //配置不同客户端不同启动端口
        ConfigureUrls(configuration);
        //配置自动映射
        ConfigureAutoMapper(context);
        //配置swagger
        ConfigureSwagger(context.Services, configuration);
        //配置自动api控制器
        ConfigureAutoApiControllers();
        //配置虚拟文件
        ConfigureVirtualFiles(hostingEnvironment);
        //配置本地化
        ConfigureLocalization();
        //配置跨域
        ConfigureCors(context, configuration);
        //配置数据加密
        ConfigureDataProtection(context);
        //配置efcore
        ConfigureEfCore(context);
        //配置自定义异常
        ConfigurException(context);
        //配置自定义权限
        ConfigurPermission(context);
        //配置应用模块模型前缀
        ConfigurModelProix(context);
        //配置自定义控制器
        ConfigurControllers(context.Services, configuration);
        //配置日志
        ConfigurLogging(context.Services);
    }

    private void ConfigurLogging(IServiceCollection services)
    {
        ////配置日志工厂
        //services.AddLogging(x =>
        //{
        //    x.AddConfiguration(services.GetConfiguration().GetSection("Logging"));
        //    //x.AddLog4Net("log4Net.config");
        //    x.AddConsole();
        //});
    }

    private void ConfigurControllers(IServiceCollection services, IConfiguration configuration)
    {
        // Add services to the container.
        services.AddControllers(option =>
        {
            //添加自定义过滤器
            option.Filters.Add<CustomExceptionFilter>();
            //取消默认的xss攻击
            option.Filters.Add(new IgnoreAntiforgeryTokenAttribute());
        });
        //servicesout configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
        services.AddEndpointsApiExplorer();
    }

    private void ConfigurModelProix(ServiceConfigurationContext context)
    {
        //配置应用权限模块前缀
        AbpPermissionManagementDbProperties.DbTablePrefix = "YDT_";

        //配置应用身份认证模块前缀
        AbpIdentityDbProperties.DbTablePrefix = "YDT_";

        //配置应用租户模块
        AbpTenantManagementDbProperties.DbTablePrefix = "YDT_";

        //配置审计日志模块
        AbpAuditLoggingDbProperties.DbTablePrefix = "YDT_";
    }

    private void ConfigurPermission(ServiceConfigurationContext context)
    {
        ////配置自定义鉴权类
        Configure<AbpPermissionOptions>(option =>
        {
            //当注册多个权限值提供者时，PermissionGrantResult.Prohibited;
            //当其中有一个返回值为Prohibited，会直接返回403，授权没有通过
            //当返回值没有Prohibited,其中有一个返回值为Granted,则代表授权通过
            //当返回值全部为Undefined,,则代表授权没有通过
            option.ValueProviders.Add(typeof(CustomPermissionValueProvider));
            option.ValueProviders.Add(typeof(ProductPermissionValueProvider));
        });

        ////取消所有的权限校验
        //context.Services.AddAlwaysAllowAuthorization();
    }

    private void ConfigurException(ServiceConfigurationContext context)
    {
        //配置Api控制器异常
        Configure<AbpExceptionHandlingOptions>(oprion =>
        {
            //显示具体异常信息
            oprion.SendExceptionsDetailsToClients = true;
            //不显示异常堆栈
            oprion.SendStackTraceToClients = false;
        });

        //配置Api控制器异常响应状态码
        Configure<AbpExceptionHttpStatusCodeOptions>(option =>
        {
            //异常编码与响应编码映射
            option.Map("-1", HttpStatusCode.TooManyRequests);
            option.Map("2", HttpStatusCode.InternalServerError);
            option.Map("3", HttpStatusCode.NotImplemented);
            option.Map("4", HttpStatusCode.BadRequest);
            option.Map("xxx", HttpStatusCode.OK);
        });
    }

    private void ConfigureAuthentication(ServiceConfigurationContext context)
    {
        //context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
        context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
        {
            options.IsDynamicClaimsEnabled = true;
        });

        //设置  Cookie 身份认证
        //在 ASP.NET Core 中，当你使用 AddAuthentication().AddCookie() 添加 Cookie 认证时，并没有直接指定一个具体的加密算法用于加密 Cookie 中的数据。
        //相反，ASP.NET Core 使用了 .NET Core 提供的 Data Protection API 来处理敏感数据的保护，这包括加密和签名。
        //Data Protection API 使用的是一种基于密钥的加密机制，但具体的加密算法（如 AES、RSA 等）是抽象化的，并且可能会随着.NET Core 版本的更新而更改。
        //Data Protection API 的目标是提供一个易于使用的 API，允许开发人员加密和验证数据，而无需深入了解底层加密机制。
        //context.Services.AddAuthentication(option =>
        //{
        //    //配置jwt认证方案
        //    //var deault = option.DefaultAuthenticateScheme;
        //    //var defalut = option.DefaultChallengeScheme;
        //}).AddCookie(option =>
        //{
        //    //option.Cookie.Name = "test";
        //    //option.ExpireTimeSpan = TimeSpan.FromMinutes(60);
        //});
        //context.Services.AddAuthentication().AddCookie();

        //配置 jwt 身份认证
        //IConfiguration configuration = services.GetConfiguration();
        //string? jwtSecurity = configuration.GetSection("configuration").Value;
        //services.AddAuthentication(option =>
        //{
        //    //配置jwt认证方案
        //    var deault = option.DefaultAuthenticateScheme;
        //    option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        //    var defalut = option.DefaultChallengeScheme;
        //    option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        //}).AddJwtBearer(option =>
        //{
        //    //配置token认证方式  设置哪些字段为true 代表验证哪些字段
        //    option.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
        //    {
        //        ValidateIssuer = true, //认证签发者 。生产token系统
        //        ValidateAudience = true,//认证受用者 erp系统
        //                                //ValidateLifetime = true,//认证生成token时间
        //        ValidateIssuerSigningKey = true,
        //        ValidIssuer = "jwt", //认证签发者 。生产token系统
        //        ValidAudience = "BasicModuleWebApiJWTAuthorization", //认证受用者 erp系统
        //        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSecurity ?? "R2XMnd6TQdYRUmR0cCfL9fk651Fb3kDI")) //认证生成token密钥
        //    };
        //});

        //设置权限
        //context.Services.AddAuthorization();
    }

    private void ConfigureBundles()
    {
        Configure<AbpBundlingOptions>(options =>
        {
            options.StyleBundles.Configure(
                LeptonXLiteThemeBundles.Styles.Global,
                bundle => { bundle.AddFiles("/global-styles.css"); }
            );
        });
    }

    private void ConfigureMultiTenancy()
    {
        Configure<AbpMultiTenancyOptions>(options =>
        {
            options.IsEnabled = IsMultiTenant;
        });
    }

    private void ConfigureUrls(IConfiguration configuration)
    {
        Configure<AppUrlOptions>(options =>
        {
            options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"];
            options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"]?.Split(',') ?? Array.Empty<string>());

            options.Applications["Angular"].RootUrl = configuration["App:ClientUrl"];
            options.Applications["Angular"].Urls[AccountUrlNames.PasswordReset] = "account/reset-password";
        });
    }

    private void ConfigureLocalization()
    {
        Configure<AbpLocalizationOptions>(options =>
        {
            options.Resources
                .Add<YDT_ABPvNext_ProjectsResource>("en")
                .AddBaseTypes(typeof(AbpValidationResource))
                .AddVirtualJson("/Localization/YDT_ABPvNext_Projects");

            options.DefaultResourceType = typeof(YDT_ABPvNext_ProjectsResource);

            options.Languages.Add(new LanguageInfo("en", "en", "English"));
            options.Languages.Add(new LanguageInfo("tr", "tr", "Türkçe"));
            options.Languages.Add(new LanguageInfo("ar", "ar", "العربية"));
            options.Languages.Add(new LanguageInfo("cs", "cs", "Čeština"));
            options.Languages.Add(new LanguageInfo("en-GB", "en-GB", "English (UK)"));
            options.Languages.Add(new LanguageInfo("hu", "hu", "Magyar"));
            options.Languages.Add(new LanguageInfo("fi", "fi", "Finnish"));
            options.Languages.Add(new LanguageInfo("fr", "fr", "Français"));
            options.Languages.Add(new LanguageInfo("hi", "hi", "Hindi"));
            options.Languages.Add(new LanguageInfo("is", "is", "Icelandic"));
            options.Languages.Add(new LanguageInfo("it", "it", "Italiano"));
            options.Languages.Add(new LanguageInfo("pt-BR", "pt-BR", "Português"));
            options.Languages.Add(new LanguageInfo("ro-RO", "ro-RO", "Română"));
            options.Languages.Add(new LanguageInfo("ru", "ru", "Русский"));
            options.Languages.Add(new LanguageInfo("sk", "sk", "Slovak"));
            options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文"));
            options.Languages.Add(new LanguageInfo("zh-Hant", "zh-Hant", "繁體中文"));
            options.Languages.Add(new LanguageInfo("de-DE", "de-DE", "Deutsch"));
            options.Languages.Add(new LanguageInfo("es", "es", "Español"));
            options.Languages.Add(new LanguageInfo("el", "el", "Ελληνικά"));
        });

        Configure<AbpExceptionLocalizationOptions>(options =>
        {
            options.MapCodeNamespace("YDT_ABPvNext_Projects", typeof(YDT_ABPvNext_ProjectsResource));
        });
    }

    private void ConfigureVirtualFiles(IWebHostEnvironment hostingEnvironment)
    {
        Configure<AbpVirtualFileSystemOptions>(options =>
        {
            options.FileSets.AddEmbedded<YDT_ABPvNext_ProjectsModule>();
            if (hostingEnvironment.IsDevelopment())
            {
                /* Using physical files in development, so we don't need to recompile on changes */
                options.FileSets.ReplaceEmbeddedByPhysical<YDT_ABPvNext_ProjectsModule>(hostingEnvironment.ContentRootPath);
            }
        });
    }

    private void ConfigureAutoApiControllers()
    {
        //Configure<AbpAspNetCoreMvcOptions>(options =>
        //{
        //    options.ConventionalControllers.Create(typeof(YDT_ABPvNext_ProjectsModule).Assembly);
        //});

        //配置AspNetCoreMvc模块
        Configure<AbpAspNetCoreMvcOptions>(option =>
        {
            //自动APi控制器
            //加载项目程序集  
            option.ConventionalControllers.Create(typeof(YDT_ABPvNext_ProjectsModule).Assembly, option =>
            {
                //修改自动api控制器的根路径(abp)  //配置改造
                option.RootPath = "projects";
            });
            //忽略手动api控制器
            option.ControllersToRemove.Add(typeof(ProductImageController));

            //开启全局自动验证dto ，验证参数是否符合规范  默认配置为true
            //关闭之后，可以使用IValidationEnabled接口类局部开启
            //局部验证 -> 全局验证 -> 自动api验证或者手动api验证（默认）
            option.AutoModelValidation = false;
        });
    }

    private void ConfigureSwagger(IServiceCollection services, IConfiguration configuration)
    {
        services.AddAbpSwaggerGenWithOAuth(
            configuration["AuthServer:Authority"]!,
            new Dictionary<string, string>
            {
                    {"YDT_ABPvNext_Projects", "YDT_ABPvNext_Projects API"}
            },
            options =>
            {
                options.SwaggerDoc("v1", new OpenApiInfo { Title = "YDT_ABPvNext_Projects API", Version = "v1" });
                options.DocInclusionPredicate((docName, description) => true);
                options.CustomSchemaIds(type => type.FullName);
            });

        ////services.AddSwaggerGen();
        //services.AddAbpSwaggerGen(option =>
        //{
        //    //显示Abp自动api接口
        //    option.DocInclusionPredicate((x, y) => true);
        //});
    }

    private void ConfigureAutoMapper(ServiceConfigurationContext context)
    {
        //context.Services.AddAutoMapperObjectMapper<YDT_ABPvNext_ProjectsModule>();
        //Configure<AbpAutoMapperOptions>(options =>
        //{
        //    /* Uncomment `validate: true` if you want to enable the Configuration Validation feature.
        //     * See AutoMapper's documentation to learn what it is:
        //     * https://docs.automapper.org/en/stable/Configuration-validation.html
        //     */
        //    options.AddMaps<YDT_ABPvNext_ProjectsModule>(/* validate: true */);
        //});

        Configure<AbpAutoMapperOptions>(option => option.AddMaps<YDT_ABPvNext_ProjectsModule>());
    }

    private void ConfigureCors(ServiceConfigurationContext context, IConfiguration configuration)
    {
        context.Services.AddCors(options =>
        {
            options.AddDefaultPolicy(builder =>
            {
                builder
                    .WithOrigins(
                        configuration["App:CorsOrigins"]?
                            .Split(",", StringSplitOptions.RemoveEmptyEntries)
                            .Select(o => o.RemovePostFix("/"))
                            .ToArray() ?? Array.Empty<string>()
                    )
                    .WithAbpExposedHeaders()
                    .SetIsOriginAllowedToAllowWildcardSubdomains()
                    .AllowAnyHeader()
                    .AllowAnyMethod()
                    .AllowCredentials();
            });
        });
    }

    private void ConfigureDataProtection(ServiceConfigurationContext context)
    {
        context.Services.AddDataProtection().SetApplicationName("YDT_ABPvNext_Projects");
    }

    private void ConfigureEfCore(ServiceConfigurationContext context)
    {
        context.Services.AddAbpDbContext<YDT_ABPvNext_ProjectsDbContext>(options =>
        {
            /* You can remove "includeAllEntities: true" to create
             * default repositories only for aggregate roots
             * Documentation: https://docs.abp.io/en/abp/latest/Entity-Framework-Core#add-default-repositories
             */
            options.AddDefaultRepositories(includeAllEntities: true);
        });

        //Configure<AbpDbContextOptions>(options =>
        //{
        //    options.Configure(configurationContext =>
        //    {
        //        configurationContext.UseMySQL();
        //    });
        //});

        //1、配置EfCoreContext
        context.Services.AddDbContext<ProductDbContext>(options =>
        {
            // 2、配置MySQL
            options.UseMySql("server=192.168.60.129;port=3306;database=AspNetCore_ORMs;uid=root;pwd=123456",
                 new MySqlServerVersion(new Version(5, 7, 29)));
        });

        //注册全集上下文 ProjectDbContext
        context.Services.AddAbpDbContext<ProjectDbContext>();

        //注册商品图片上下文
        context.Services.AddAbpDbContext<ProductImageDbContext>();

        //IConfiguration configuration = services.GetConfiguration();

        //注册订单上下文
        //services.AddAbpDbContext<OrderDbContext>();

        //集成mysql   
        Configure<AbpDbContextOptions>(option =>
        {
            option.UseMySQL();
            //var config = configuration.GetSection("DatabaseType").Value;
            //Console.WriteLine(config);
            ////动态切换数据
            //if (config.Equals("SqlServer"))
            //{
            //    option.UseSqlServer();
            //}
            //else
            //{
            //    option.UseMySQL();
            //}
        });

    }

    public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        var app = context.GetApplicationBuilder();
        var env = context.GetEnvironment();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseAbpRequestLocalization();

        if (!env.IsDevelopment())
        {
            app.UseErrorPage();
        }

        app.UseCorrelationId();
        app.UseStaticFiles();
        app.UseRouting();
        app.UseCors();
        app.UseAuthentication();
        //app.UseAbpOpenIddictValidation();

        if (IsMultiTenant)
        {
            app.UseMultiTenancy();
        }

        app.UseUnitOfWork();
        app.UseDynamicClaims();
        app.UseAuthorization();

        app.UseSwagger();
        app.UseAbpSwaggerUI(options =>
        {
            options.SwaggerEndpoint("/swagger/v1/swagger.json", "YDT_ABPvNext_Projects API");

            var configuration = context.GetConfiguration();
            options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);
            options.OAuthScopes("YDT_ABPvNext_Projects");
        });

        app.UseAuditing();
        app.UseAbpSerilogEnrichers();
        app.UseConfiguredEndpoints();
    }
}
